package com.kernowsoft.wavefx;

import java.util.ArrayList;
import java.util.HashMap;

import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.BaseColumns;
import android.util.Log;

public class WaveDataContentProvider extends ContentProvider {

	private static final String TAG = WaveDataContentProvider.class
			.getSimpleName();

	// database helper instances:
	LocationsDBOpenHelper dbLocationsOpenHelper;

	public static final String DATABASE_NAME = "wavedata.db";

	// inner class for defining wave forecast data:
	public static final class SiteForecast extends Object implements
			BaseColumns {
		// BaseColumns provides _ID and _COUNT constants.

		// Content URI:
		public static final Uri CONTENT_URI = Uri
				.parse("content://com.kernowsoft.wavefx/site_data");
		public static final Uri VALIDTIME_URI = Uri
				.parse("content://com.kernowsoft.wavefx/valid_time");

		// Table column names:
		// _ID inherited from BaseColumns
		public static final String SITENAME = "name";
		public static final String VALID_DATETIME = "valid_datetime";
		public static final String FORECAST_PERIOD = "forecast_period";
		public static final String WAVE_HSIG1 = "wave_hsig1";
		public static final String WAVE_HSIG2 = "wave_hsig2";
		public static final String WAVE_HSIG3 = "wave_hsig3";
		public static final String WAVE_TP1 = "wave_tp1";
		public static final String WAVE_TP2 = "wave_tp2";
		public static final String WAVE_TP3 = "wave_tp3";
		public static final String WAVE_TM1 = "wave_tm1";
		public static final String WAVE_TM2 = "wave_tm2";
		public static final String WAVE_TM3 = "wave_tm3";
		public static final String WAVE_MDIR1 = "wave_mdir1";
		public static final String WAVE_MDIR2 = "wave_mdir2";
		public static final String WAVE_MDIR3 = "wave_mdir3";
		public static final String WIND_SPD = "wind_spd";
		public static final String WIND_DIR = "wind_dir";

		public static final String DATABASE_TABLE = "forecast_data";

		public static final String TABLE_CREATE = "CREATE TABLE "
				+ DATABASE_TABLE + " (" + _ID
				+ " integer primary key autoincrement, " + SITENAME
				+ " text not null, " + VALID_DATETIME + " text not null, "
				+ FORECAST_PERIOD + " integer not null, " + WAVE_HSIG1
				+ " float default null, " + WAVE_HSIG2
				+ " float default null, " + WAVE_HSIG3
				+ " float default null, " + WAVE_TM1 + " float default null, "
				+ WAVE_TM2 + " float default null, " + WAVE_TM3
				+ " float default null, " + WAVE_TP1 + " float default null, "
				+ WAVE_TP2 + " float default null, " + WAVE_TP3
				+ " float default null, " + WAVE_MDIR1
				+ " float default null, " + WAVE_MDIR2
				+ " float default null, " + WAVE_MDIR3
				+ " float default null, " + WIND_SPD + " float not null, "
				+ WIND_DIR + " float not null " + ")";

		public static final String TABLE_CREATE_VALID_TIMES = "CREATE TABLE "
				+ " valid_time ( " + _ID
				+ " integer primary key autoincrement," + SITENAME
				+ " text not null, " + VALID_DATETIME + " text not null)";

	}

	public static final class Locations extends Object {
		// Expose content URI to ContentResolver:
		public static final Uri CONTENT_URI = Uri
				.parse("content://com.kernowsoft.wavefx/locations");

		// Table column names:
		public static final String _ID = "_id";
		public static final String NAME = "name";
		public static final String LAT = "lat";
		public static final String LON = "lon";

		public static final String DATABASE_TABLE = "forecast_locations";

		public static final String TABLE_CREATE = "create table "
				+ DATABASE_TABLE + " (" + _ID
				+ " integer primary key autoincrement, " + NAME
				+ " text not null, " + LAT + " float not null, " + LON
				+ " float not null)";

	}

	// Static UriMatcher, for determining if we are requesting all rows, a
	// single row, or a search request:
	private static final int ALL_ROWS = 1; // site data, all rows
	private static final int SINGLE_ROW = 2; // site data, single row
	private static final int SEARCH = 3; // site data, search

	private static final int FX_ALL_ROWS = 10; // forecast data, all rows
	private static final int FX_SINGLE_ROW = 11; // "" single row

	private static final int FX_VALIDITY_TIME = 12;

	private static UriMatcher uriMatcher;
	static {
		uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

		// site names:
		uriMatcher.addURI("com.kernowsoft.wavefx", "locations", ALL_ROWS);
		uriMatcher.addURI("com.kernowsoft.wavefx", "locations/#", SINGLE_ROW);

		// matchers for auto complete of site names
		uriMatcher.addURI("com.kernowsoft.wavefx",
				SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH);
		uriMatcher.addURI("com.kernowsoft.wavefx",
				SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH);
		uriMatcher.addURI("com.kernowsoft.wavefx",
				SearchManager.SUGGEST_URI_PATH_SHORTCUT, SEARCH);
		uriMatcher.addURI("com.kernowsoft.wavefx",
				SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", SEARCH);

		// wave forecast data:
		uriMatcher.addURI("com.kernowsoft.wavefx", "site_data", FX_ALL_ROWS);
		uriMatcher.addURI("com.kernowsoft.wavefx", "site_data/#",
				FX_SINGLE_ROW);

		// validity time:
		uriMatcher.addURI("com.kernowsoft.wavefx", "valid_time",
				FX_VALIDITY_TIME);
	}

	// Search suggestion projection map (static):
	private static final HashMap<String, String> SEARCH_SUGGEST_PROJECTION_MAP;
	static {
		SEARCH_SUGGEST_PROJECTION_MAP = new HashMap<String, String>();

		SEARCH_SUGGEST_PROJECTION_MAP
				.put("_id", Locations._ID + " as " + "_id");

		SEARCH_SUGGEST_PROJECTION_MAP.put(SearchManager.SUGGEST_COLUMN_TEXT_1,
				Locations.NAME + " as " + SearchManager.SUGGEST_COLUMN_TEXT_1);

		SEARCH_SUGGEST_PROJECTION_MAP.put(
				SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, Locations._ID
						+ " AS " + "_id");
	}

	@Override
	public int delete(Uri uri, String where, String[] whereArgs) {
		SQLiteDatabase db;
		db = dbLocationsOpenHelper.getWritableDatabase();
		String table;

		Log.d(TAG, "Chris: Deleting values from database: " + uri.toString());
		// TODO: handle different tables:
		switch (uriMatcher.match(uri)) {
		case ALL_ROWS:
		case SINGLE_ROW:
			table = Locations.DATABASE_TABLE;
			break;

		case FX_ALL_ROWS:
		case FX_SINGLE_ROW:
			table = SiteForecast.DATABASE_TABLE;
			break;

		default:
			throw new IllegalArgumentException("Unsupported URI: " + uri);
		}

		return db.delete(table, where, whereArgs);

	}

	@Override
	public String getType(Uri uri) {
		// return the MIME type of the data (vnd.android.cursor.item)
		// Will be different for single row and all rows:
		switch (uriMatcher.match(uri)) {
		case ALL_ROWS:
			return "vnd.android.cursor.dir/vnd.kernowsoft.locations";
		case SINGLE_ROW:
			return "vnd.android.cursor.item/vnd.kernowsoft.locations";
		case SEARCH:
			return SearchManager.SUGGEST_MIME_TYPE;
		case FX_ALL_ROWS:
			return "vnd.android.cursor.dir/vnd.kernowsoft.site_data";
		case FX_SINGLE_ROW:
			return "vnd.android.cursor.item/vnd.kernowsoft.site_data";
		default:
			throw new IllegalArgumentException("Unsupported URI: " + uri);
		}

	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		SQLiteDatabase db;
		db = dbLocationsOpenHelper.getWritableDatabase();
		String table;

		// TODO: handle different tables:
		switch (uriMatcher.match(uri)) {
		case ALL_ROWS:
		case SINGLE_ROW:
			table = Locations.DATABASE_TABLE;
			break;

		case FX_ALL_ROWS:
		case FX_SINGLE_ROW:
			table = SiteForecast.DATABASE_TABLE;
			break;

		default:
			throw new IllegalArgumentException("Unsupported URI: " + uri);
		}

		long id = db.insert(table, null, values);

		// get Uri of inserted row:
		if (id > -1) {
			Uri insertedUri = ContentUris.withAppendedId(uri, id);

			// let content resolver know that a change has happened:
			getContext().getContentResolver().notifyChange(insertedUri, null);
			return insertedUri;
		}

		// insert failed:
		return null;
	}

	@Override
	public boolean onCreate() {
		// initialise helpers:
		dbLocationsOpenHelper = new LocationsDBOpenHelper(getContext());
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {

		String rowId;
		SQLiteDatabase db;
		Uri notify = null;

		// get readable database
		db = dbLocationsOpenHelper.getReadableDatabase();

		// use SQLite Query Builder to simplify constructing database query:
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();

		switch (uriMatcher.match(uri)) {
		case ALL_ROWS:
			queryBuilder.setTables(Locations.DATABASE_TABLE);
			notify = Locations.CONTENT_URI;
			break;

		case SINGLE_ROW:
			rowId = uri.getPathSegments().get(1);
			queryBuilder.appendWhere(Locations._ID + " = " + rowId);
			queryBuilder.setTables(Locations.DATABASE_TABLE);
			notify = Locations.CONTENT_URI;
			break;

		case SEARCH:
			String query = uri.getPathSegments().get(1);
			queryBuilder.appendWhere(Locations.NAME + " LIKE \"%" + query
					+ "%\"");
			queryBuilder.setTables(Locations.DATABASE_TABLE);

			// search manager expects results to be provided in columns of
			// certain names - use projection map to translate table column
			// names (essentially SQLite "AS" statements):
			queryBuilder.setProjectionMap(SEARCH_SUGGEST_PROJECTION_MAP);

			break;

		case FX_ALL_ROWS:
			//Log.d(TAG, "Chris: Query - all rows from forecast data");
			queryBuilder.setTables(SiteForecast.DATABASE_TABLE);
			notify = SiteForecast.CONTENT_URI;
			break;

		case FX_SINGLE_ROW:
			//Log.d(TAG, "Chris: Query - single row from forecast data");
			rowId = uri.getPathSegments().get(1);
			queryBuilder.appendWhere(SiteForecast._ID + " = " + rowId);
			queryBuilder.setTables(SiteForecast.DATABASE_TABLE);
			notify = SiteForecast.CONTENT_URI;
			break;

		case FX_VALIDITY_TIME:
			//Log.d(TAG, "ContentProvider: Requesting validity time");
			queryBuilder.setTables("valid_time");
			break;

		default:
			throw new IllegalArgumentException("Unsupported URI: " + uri);
		}

		String groupBy = null;
		String having = null;

		// execute query:
		Cursor cursor = queryBuilder.query(db, projection, selection,
				selectionArgs, groupBy, having, sortOrder);

		// make the cursor listen to changes to the content
		if (notify != null)
			cursor.setNotificationUri(getContext().getContentResolver(), notify);

		return cursor;

	}

	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
		String strTable;
		
		// get readable database
		SQLiteDatabase db;
		db = dbLocationsOpenHelper.getReadableDatabase();

		switch(uriMatcher.match(uri)) {
		case FX_VALIDITY_TIME:
			strTable = "valid_time";
			break;
		default:
			Log.d(TAG, "Insert: unknown table");
			return 0;
		}
		
		return db.update(strTable, values, selection, selectionArgs);
	}

	private static class LocationsDBOpenHelper extends SQLiteOpenHelper {

		public static final String DATABASE_NAME = "wavedata.db";
		public static final int DATABASE_VERSION = 1;

		public LocationsDBOpenHelper(Context context) {
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			// Called when no database exists:

			// create locations table
			db.execSQL(Locations.TABLE_CREATE);

			// create forecast data table:
			db.execSQL(SiteForecast.TABLE_CREATE);

			// create valid_times table:
			db.execSQL(SiteForecast.TABLE_CREATE_VALID_TIMES);

			ArrayList<String> sites = new ArrayList<String>();
			sites.add("Croyde");
			sites.add("Perran");
			sites.add("Mountsbay");
			sites.add("Ekofisk");

			for (int i = 0; i < sites.size(); i++) {
				db.execSQL("INSERT INTO valid_time" + " ("
						+ SiteForecast.SITENAME + ", "
						+ SiteForecast.VALID_DATETIME + ") VALUES ('"
						+ sites.get(i) + "', '0')");
			}
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			// delete old table:
			db.execSQL("DROP TABLE IF EXISTS " + Locations.DATABASE_TABLE);
			db.execSQL("DROP TABLE IF EXISTS " + SiteForecast.DATABASE_TABLE);
			db.execSQL("DROP TABLE IF EXISTS " + "valid_time");

			// create new one:
			onCreate(db);
		}
	} // LocationsDBOpenHelper

}